/*
    日志的三种落地：
        标准输出：StdoutSink
        固定⽂件：FileSink
        滚动⽂件：RollSink
*/
#ifndef __M_SINK_H__
#define __M_SINK_H__

#include "util.hpp"
#include <iostream>
#include <fstream>
#include <string>
#include <memory>
#include <cassert>
#include <sstream>
#include <ctime>

namespace Log
{
    class LogSink
    {
    public:
        using prt = std::shared_ptr<LogSink>;

        LogSink(){}
        virtual ~LogSink(){}
        // 落地方式
        virtual void log(const std::string& data) = 0;
    };
    
    // 标准输出
    class StdoutSink:public LogSink
    {
    public:
        StdoutSink(){}
        void log(const std::string& data)override
        {
            std::cout.write(data.c_str(),data.size());
        }
    };

    // 指定文件
    class FileSink:public LogSink
    {
    public:
        FileSink(std::string filename)
            :_filename(filename)
        {
            // 1. 创建文件目录
            util::File::CreateDirectory(util::File::Path(_filename));

            // 2. 创建文件
            _ofs.open(_filename.c_str(),std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }
        void log(const std::string& data)override
        {
            _ofs.write(data.c_str(),data.size());
        }
    private:
        std::string _filename;      // 文件名
        std::ofstream _ofs;
    };

    // 滚动文件
    class RollSink:public LogSink
    {
    public:
        RollSink(std::string basename,size_t maxsize)
            :_basename(basename)
            ,_maxsize(maxsize)
            ,_count(0)
            ,_cursize(0)
        {
            util::File::CreateDirectory(util::File::Path(_basename));

            std::string _filename = GetFileName();
            _ofs.open(_filename.c_str(),std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }
        void log(const std::string& data)override
        {
            // 1. 先判断文件大小是否在范围内
            CreateNewFile();

            // 2. 打开文件（如果不存在就创建）
            _ofs.write(data.c_str(),data.size());
            assert(_ofs.good());
            _cursize += data.size();
        }
    private:
        void CreateNewFile()
        {
            // 文件未打开或者当前大小 >= 最大值
            if(_ofs.is_open() == false || _cursize >= _maxsize)
            {
                // 1. 关闭已写满的文件
                _ofs.close();

                // 创建并打开新文件
                std::string _filename = GetFileName();
                _ofs.open(_filename.c_str(),std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
                _cursize = 0;

                return;
            }
        }
        std::string GetFileName()
        {
            std::stringstream ss;
            time_t te = util::Data::GetNowTime();
            struct tm t;
            localtime_r(&te,&t);
            
            ss.clear();
            ss << _basename;
            ss << "log";
            ss << t.tm_year + 1900;
            ss << t.tm_mon + 1;
            ss << t.tm_mday;
            ss << t.tm_hour;
            ss << t.tm_min;
            ss << t.tm_sec;
            ss << "_";
            ss << std::to_string(_count++);
            ss << ".log";

            return ss.str();
        }
    private:
        std::string _basename;      // 文件基础名，文件全名：文件基础名+log+时间+".log"
        size_t _cursize;            // 当前容量
        size_t _maxsize;            // 最大容量
        size_t _count;              // 文件数
        std::ofstream _ofs;
    };

    class SinkFactor
    {
    public:
        // SinkType：落地方式
        // ...Args：传入的不定参数
        template<typename SinkType, typename ...Args>
        static LogSink::prt Create(Args&& ...args)     // 这里是万能引用，可以接收左值和右值
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };
}



#endif